#include "tool.h"
#include "net_pan.h"
#include "work.h"
int ID_PASSWORD_handle(int netfd,char* curpwd,message_t* message){
    int ret;
    //密码验证
    ret=id_test(message->path2,message->path1);//-1为密码错误,0为密码正确(后期可以增加用户不存在)
    if(ret==-1){
        //返回密码错误
        train_t train;
        train.length=-1;
        strcpy(train.data,"The password is wrong!");
        send(netfd,&train,sizeof(train),0);
    }else if(ret==0){
        //返回密码正确
        train_t train;
        train.length=0;
        strcpy(train.data,"Log in success!");
        send(netfd,&train,sizeof(train),0);
        //设置当前路径        
        strcpy(curpwd,"./client_dir/");
        strcat(curpwd,message->path2);
        mkdir(curpwd,0777);//假设每次登录的都是新用户
    }
    return ret;
}

int CD_handle(char *ID,int netfd,message_t* message,char* curpwd,char* rootpwd,char* lastpwd){
    if(message->path1[0]=='-'){//切换到上一次工作目录
        strcpy(curpwd,lastpwd);
    }else
    if((message->path1[0]=='/'&&message->path1[1]=='\0')||strlen(message->path1)==0||
      (message->path1[0]=='~'&&message->path1[1]=='\0')){//切换到家目录；
        strcpy(lastpwd,curpwd);//更新上一次工作目录
        strcpy(curpwd,rootpwd);
    }else
    if(message->path1[0]=='.'&&message->path1[1]=='.'){//切换到父目录
        if(strcmp(curpwd,rootpwd)==0)return 0;
        strcpy(lastpwd,curpwd);
        int i=strlen(curpwd);
        while(curpwd[i]!='/'){
            i--;
        }
        curpwd[i]='\0'; 
    }else
    if(message->path1[0]=='.'&&message->path1[1]=='/'){//从当前路径出发
       char a[1024];
       bzero(a,sizeof(a));
       strcpy(a,curpwd);
       strcat(curpwd,&message->path1[1]);
       DIR* ret=opendir(curpwd);
       if(ret==NULL){//说明该目录不存在
           strcpy(curpwd,a);
           return -1;
           SYSLOG(ID,"CD",curpwd,"fail");
       }else{
           strcpy(lastpwd,a);
           SYSLOG(ID,"CD",curpwd,"success");
       }
    }else
    if(message->path1[0]=='/'){//从绝对路径出发
        printf("come here\n");
        char a[1024];
        bzero(a,sizeof(a));
        strcpy(a,rootpwd);
        strcat(a,message->path1);
        DIR* ret=opendir(a);
        if(ret==NULL){//路径不存在
            SYSLOG(ID,"CD",curpwd,"fail");
            return -1;
        }else{
            strcpy(lastpwd,curpwd);
            strcpy(curpwd,a);
            SYSLOG(ID,"CD",curpwd,"success");
        }        
    }
    
    return 0;
}

int PWD_handle(int netfd,char* curpwd){
    train_t train;
    train.length=0;
    char a[1024]="./client_dir";
    char str[1024];
    bzero(str,sizeof(str));
    strcpy(str,curpwd);
    int i=0;
    while(str[i]==a[i]){
        i++;
    }
    strcpy(train.data,&str[i]);
    send(netfd,&train,sizeof(train),0);
    printf("data = %s\n",train.data);
    return 0;
}

int LS_handle(char* ID,const char *path, int netfd) {

    train_t train;

    bzero(train.data, sizeof(train.data));
    DIR *dirp = opendir(path);
    if (!dirp){
        train.length = -1;
        send(netfd, &train.length, sizeof(int), 0);
        sprintf(train.data, "opendir failed!");
        send(netfd, train.data, strlen(train.data), 0);
        SYSLOG(ID, "LS", path,"fail");
        return -1;
    }

    struct dirent *p_dirent;
    bzero(train.data, sizeof(train.data));
    char info[4096] = {0};

    while ((p_dirent = readdir(dirp)) != NULL) {
        if ((strcmp(p_dirent->d_name, ".") == 0) || (strcmp(p_dirent->d_name, "..") == 0)) {
            continue;
        }

        sprintf(info, "%s%c", p_dirent->d_name, ' ');
        strcat(train.data, info);
    }
    train.length = 0;
    send(netfd,&train,sizeof(train),0);
    SYSLOG(ID, "LS", path,"success");
    closedir(dirp);
    return 0;
}

int MKDIR_handle(char* ID,const char* path,int netfd){
    if(mkdir(path,0777)){
        train_t train;
        train.length=-1;
        bzero(train.data,sizeof(train.data));
        strcpy(train.data,"MKDIR is fault.");
        send(netfd,&train,sizeof(train),0);
        SYSLOG(ID,"MKDIR",path,"fault");
    }
    else{
        train_t train;
        train.length=0;
        bzero(train.data,sizeof(train.data));
        strcpy(train.data,"makeDir is success.");
        send(netfd,&train,sizeof(train),0);
        SYSLOG(ID,"MKDIR",path,"success");
    }
}

int RMDIR_handle(char* ID,const char* path,int netfd){
    if(rmdir(path)){
        train_t train;
        train.length=-1;
        bzero(train.data,sizeof(train.data));
        strcpy(train.data,"RMDIR is fault.");
        send(netfd,&train,sizeof(train),0);
        SYSLOG(ID,"RMDIR",path,"fault");
    }
    else{
        train_t train;
        train.length=0;
        bzero(train.data,sizeof(train.data));
        strcpy(train.data,"RMDIR is success.");
        send(netfd,&train,sizeof(train),0);
        SYSLOG(ID,"RMDIR",path,"success");
    }
}

void get_salt(char *salt,char *passwd){
    int i,j;
    //取出salt,i记录密码字符下标,j记录$出现次数
    for(i=0,j=0;passwd[i] && j != 3;++i){
        if(passwd[i] == '$')
            ++j;
    }
    strncpy(salt,passwd,i-1);
}

int id_test(const char * p_id, const char * p_passwd) {    
    struct spwd *sp;
    char salt[1024];
    bzero(salt,sizeof(salt));
    if((sp=getspnam(p_id)) == NULL) {
        return -1;
    }else {
        get_salt(salt,sp->sp_pwdp);
    }    

    if(strcmp(sp->sp_pwdp,crypt(p_passwd,salt)) == 0){
        return 0;
    }else {
        return -1;
    }
}

int GETS_handle(char* ID,int netfd, const char* path){

    train_t train;
    memset(&train,0,sizeof(train));
    
    int fd = open(path, O_RDWR);
    if(fd == -1){
        train.length = -1;
        memcpy(train.data,"ERROR:NO FILE!",14);
        send(netfd,&train,sizeof(train_t),MSG_NOSIGNAL);
        SYSLOG(ID,"GETS",path,"FAIL:NO FILE!");
        return -1;
    }
    else{
        train.length = 0;
        memcpy(train.data,"OPEN SUCCESS!",13);
        send(netfd,&train,sizeof(train_t),MSG_NOSIGNAL);
    }

    //接收已存在文件的大小
    off_t oldsize = 0;//传输前已存在的文件大小
    int length = 0;
    ssize_t sret = recv(netfd, &length, sizeof(length), 0); 
    sret = recv(netfd, &oldsize, length, 0);
    
    //train.length = strlen(filename);
    //memcpy(train.data,filename,train.length);
    //send(netfd,&train,sizeof(int)+train.length,MSG_NOSIGNAL);// 发文件的名字

    struct stat statbuf;
    fstat(fd,&statbuf);
    train.length = sizeof(statbuf.st_size);
    memcpy(train.data,&statbuf.st_size,train.length);
    send(netfd,&train,sizeof(int)+train.length,MSG_NOSIGNAL);// 发文件的大小

    //如果文件大小小于100M，直接发送
    if(statbuf.st_size < 100*1024*1024){
        lseek(fd,oldsize,SEEK_SET);
        while(1){
            bzero(&train,sizeof(train));
            ssize_t sret = read(fd,train.data,sizeof(train.data)); 
            train.length = sret;
            //即使文件已经读完了，也发一个空车厢火车过去
            //目标是让客户端知道文件已经发完了
            send(netfd,&train,sizeof(int)+train.length,MSG_NOSIGNAL);// 发文件的内容
            if(sret == 0){
                break;
            }
        }
    }
    //如果文件大小大于100M，使用mmap然后发送
    else{
        off_t totalsize = oldsize;//已传输的文件大小
        char *p = (char *)mmap(NULL,statbuf.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
        ERROR_CHECK(p,MAP_FAILED,"mmap");
        while(totalsize < statbuf.st_size){
            if(statbuf.st_size - totalsize > 1000){
                train.length = 1000;//剩余未发送的内容超过1000
            }
            else{
                train.length = statbuf.st_size - totalsize;
                //剩余内容不足1000
            }
            send(netfd,&train.length,sizeof(train.length),MSG_NOSIGNAL);//发火车头
            send(netfd,p+totalsize,train.length,MSG_NOSIGNAL);//火车车厢在mmap映射区
            totalsize += train.length;
        }
        // 补发一个空火车
        train.length = 0;
        send(netfd,&train.length,sizeof(train.length),MSG_NOSIGNAL);//发火车头
        munmap(p,statbuf.st_size);
    }
    close(fd);
    SYSLOG(ID,"GETS",path,"success");
    return 0;
}

int recvn(int sockfd, void *buf, int length){
    int total = 0;//记录已经收到的数据的长度
    char *p = (char *)buf;
    while(total < length){
        ssize_t sret = recv(sockfd,p+total,length-total,0);
        total += sret;
    }
    return total;
}

int PUTS_handle(char* ID,int netfd, const char* path){
    int fd = open(path,O_RDWR|O_CREAT,0666);
    if(fd==-1){
        train_t train;
        train.length = -1;
        memcpy(train.data,"ERROR:NO PATH!",14);
        send(netfd,&train,sizeof(train_t),MSG_NOSIGNAL);
        SYSLOG(ID,"PUTS",path,"FAIL:NO PATH!");
        return -1;
    }
    else{
        train_t train;
        train.length = 0;
        memcpy(train.data,"OPEN SUCCESS!",13);
        send(netfd,&train,sizeof(train_t),MSG_NOSIGNAL);
    }
    // 先收文件名
    char filename[1024] = {0};
    int length;
    recvn(netfd,&length,sizeof(length));
    recvn(netfd,filename,length);

    //再收文件大小
    off_t filesize = 0;
    recvn(netfd,&length,sizeof(length));
    recvn(netfd,&filesize,length);
    printf("filesize = %ld\n", filesize);

    //再收文件内容
    if(filesize<100*1024*1024){
        off_t cursize = 0;
        off_t slice = filesize/10000;
        off_t lastsize = 0;//上次打印的时候文件大小
        while(1){
            char buf[4096] = {0};
            recvn(netfd,&length,sizeof(length));
            if(length != 1000){
                printf("length = %d\n", length);
            }
            if(length == 0){
                break;
            }
            recvn(netfd,buf,length);
            write(fd,buf,length);

            cursize += length;
            if(cursize - lastsize > slice){
                printf("%5.2lf%%\r",100.0*cursize/filesize);
                fflush(stdout);//将stdout缓冲区的数据刷到文件对象里面
                lastsize = cursize;
            }
        }
        close(fd);
        printf("100.00%%\n");
    }
    else{
        ftruncate(fd,filesize);
        char *p=(char*)mmap(NULL,filesize,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
        ERROR_CHECK(p,MAP_FAILED,"mmap");
        recvn(netfd,p,filesize);
        munmap(p,filesize);
        close(fd);
        printf("100.00%%\n");
    }

    SYSLOG(ID,"PUTS",path,"success");
    return 0;
}

int REMOVE_handle(int netfd,char* ID, const char *pathname){
    printf("path = %s\n",pathname);
    int fd = open(pathname, O_RDWR);
    if (fd == -1) {
        SYSLOG(ID, "rm", pathname,"fail");
        train_t train;
        train.length=-1;
        send(netfd,&train,sizeof(train),0);
        close(fd);
        return -1;
    }

    int ret = unlink(pathname);
    if (ret == -1) {
        SYSLOG(ID, "rm", pathname,"fail");
        train_t train;
        train.length=-1;
        send(netfd,&train,sizeof(train),0);
        return -1;
    }
    SYSLOG(ID, "rm", pathname,"success");
    train_t train;
    train.length=0;
    send(netfd,&train,sizeof(train),0);
    return 0;
}
